
//
// 120331 - AH - Created.
//

package org.soekrat.server.io.db;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Id;

import org.soekrat.AttributeObject;

import com.googlecode.objectify.annotation.Entity;

/**
 * <p>
 *   Base class for 
 *   {@link DbObject}s that have parameters.  
 * </p>
 * @author Arne Halvorsen (AH)
 * @author Laetitia Le Chatton (LLC)
 */
@Entity
public class ParameterBase implements DbObject 
{
  private @Id Long id;
  
  private List<String> names=new ArrayList<String>();
  
  private List<String> values=new ArrayList<String>();

  /**
   * <p>
   *   Get key that identifies {@this}.
   * </p>
   * @return Key.
   */
  public final long id(){ return this.id; }
  
  /**
   * <p>
   *   Sets parameter given in a map where keys are parameter names and values'
   *   {@link Object#toString() string presentation} are parameter values.
   * </p>
   * <p>
   *   Parameters {@code this} has but not named in {@code params} are left
   *   unchanged.
   * </p>
   * <p>
   *   This can also delete parameters, a parameter is named in {@code params}
   *   it is deleted if
   * </p>
   * <ul>
   *   <li>
   *     the value is {@code null}.
   *   </li>
   *   <li>
   *     value is the zero length string after been trimmed.
   *   </li>
   * </ul> 
   *    but has a {@code null} value, value's string presentation
   *   is the empty string after being trimmed or the string presentation is
   *   {@code null}
   * </p>
   * @param params
   */
  public final void setParameters(Map<String, String> params)
  {
    if (params==null)
    {
      throw new NullPointerException("params");
    }
    
    for (Map.Entry<String, String> curr : params.entrySet())
    {
      String name=curr.getKey();
      String val=curr.getValue();
      String pval=(val==null) ? "" : val.trim();
      if (pval.length()==0) removeParameter(name);
      else                  setParameter(name, pval);
    }
  }
  
  /**
   * <p>
   *   Sets parameter,
   * </p>
   * @param name  Parameter name.
   * @param value Parameter value.
   */
  public final void setParameter(String name, String value)
  {
    if (name==null)
    {
      throw new NullPointerException("name");
    }
    if (value==null)
    {
      throw new NullPointerException("value");    
    }
    
    int idx=getIndexOfParameter(name);
    if (idx==-1)
    {
      this.names.add(name);
      this.values.add(value);
      return;
    }
    this.values.set(idx, value);
  }
  
  /**
   * <p>
   *   Removes parameter.
   * </p>
   * @param name Name on parameter to remove.
   * @return {@code true} if removed, {@code false} if not found.
   */
  public final boolean removeParameter(String name)
  {
    if (name==null)
    {
      throw new NullPointerException("name");
    }
    
    int idx=getIndexOfParameter(name);
    if (idx==-1) return false;
    this.names.remove(idx);
    this.values.remove(idx);
    return true;
  }
  
  /**
   * <p>
   *   Gets the number of parameters.
   * </p>
   * @return Count.
   */
  public final int getParameterCount(){ return names.size(); }
  
  /**
   * <p>
   *   Tells if has parameter.
   * </p>
   * @param name Parameter name.
   * @return {@code true} if has, {@code false} if has not.
   */
  public final boolean hasParameter(String name)
  { 
    return getIndexOfParameter(name)!=-1; 
  }
  
  /**
   * <p>
   *   Gets parameter value.
   * </p>
   * @param name Parameter name.
   * @return Value or {@code null} if not found.
   */
  public final String getParameter(String name)
  {
    if (name==null)
    {
      throw new NullPointerException("name");
    }
    
    int idx=getIndexOfParameter(name);
    return idx==-1 ? null : this.values.get(idx);
  }
  
  /**
   * <p>
   *   Gets parameters in a map where keys are parameter names and values are
   *   parameter values.
   * </p>
   * @return Map.
   */
  public final Map<String, String> getParameterMap()
  {
    return getParameterMap((String[])null);
  }
  
  /**
   * <p>
   *   Gets parameters in a map where keys are parameter names and values are
   *   parameter values with the possibility to ignore named parameters.
   * </p>
   * @param ignore Names of parameters to ignore. Can be {@code null} if to
   *               ignore non.
   * @return Map.
   */
  public final Map<String, String> getParameterMap(String... ignore)
  {
    Map<String, String> retVal=new HashMap<String, String>();
    
    int n=this.names.size();
    for (int i=0; i<n; i++)
    {
      String name=this.names.get(i);
      if (!contains(name, ignore))
      {
        retVal.put(name, this.values.get(i));
      }
    }
    
    return retVal;
  }
  
  private static boolean contains(String name, String[] names)
  {
    if (names==null) return false;
    for (String curr : names) if (name.equals(curr)) return true;  
    return false;
  }
  
  /**
   * <p>
   *   Gets index of parameter.
   * </p>
   * @param name Parameter name.
   * @return Index or {@code -1} if not found.
   */
  private int getIndexOfParameter(String name)
  {
    int n=names.size();
    for (int i=0; i<n; i++)
    {
      String curr=names.get(i);
      if (curr.equals(name)) return i;
    }
    return -1;
  }
  
  /**
   * <p>
   *   Sets this' data as attributes to a given
   *   {@link AttributeObject}.
   * </p>
   * @param ao {@code AttributeObject} to receive.
   */
  public void setAttributes(AttributeObject ao)
  {
    if (ao==null)
    {
      throw new NullPointerException("ao");
    }
    
    int n=this.names.size();
    for (int i=0; i<n; i++)
    {
      String name=this.names.get(i);
      String value=this.values.get(i);
      ao.setAttribute(name, value);
    }
  }
  
  @Override
  public String toString() 
  {
    StringBuilder sb=new StringBuilder();
   
    sb.append("parameters=[");
    int n=this.names.size();
    for (int i=0; i<n; i++)
    {
      if (i>0) sb.append(',');
      sb.append(this.names.get(i)).append('=').append(this.values.get(i));
    }
    sb.append("]");
    
    return sb.toString();
  }
  
}
